#include "stdafx.h"
#include <tinyxml.h>


static int pocitadlo=0;

void rescaleMeshPlease(Ogre::MeshPtr meshPtr,Ogre::Vector3 scale)
{ 
            // Iterate thru submeshes
            Ogre::Mesh::SubMeshIterator smit = meshPtr->getSubMeshIterator();
            while( smit.hasMoreElements() )
            {
               Ogre::SubMesh* submesh = smit.getNext();
               if(submesh)
               {
                  Ogre::VertexData* vertex_data = submesh->vertexData;
                  if(vertex_data)
                  {
                     const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
                     Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
                     unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL));
                     Ogre::Real* points;
                     
                     for(size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
                     {
                        posElem->baseVertexPointerToElement(vertex, (void **)&points);
                        points[0] *= scale.x;
                        points[1] *= scale.y;
                        points[2] *= scale.z;
                     }
                     
                     vbuf->unlock(); 
                  }
               }
            } 
}

void centerBoxPlease(Ogre::MeshPtr meshPtr,Ogre::Real heigth)
{ 

            // Iterate thru submeshes
            Ogre::Mesh::SubMeshIterator smit = meshPtr->getSubMeshIterator();
            while( smit.hasMoreElements() )
            {
               Ogre::SubMesh* submesh = smit.getNext();
               if(submesh)
               {
                  Ogre::VertexData* vertex_data = submesh->vertexData;
                  if(vertex_data)
                  {
                     const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
                     Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
                     unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL));
                     Ogre::Real* points;
                     
                     for(size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
                     {
                        posElem->baseVertexPointerToElement(vertex, (void **)&points);
                        points[1] -= (heigth/2);
                     }
                     
                     vbuf->unlock(); 
                  }
               }
            }		 
}

const TiXmlElement* IterateChildElements(const TiXmlElement* xmlElement, const TiXmlElement* childElement)
{
    if (xmlElement != 0)
    {
        if (childElement == 0)
            childElement = xmlElement->FirstChildElement();
        else
            childElement = childElement->NextSiblingElement();
        
        return childElement;
    }

    return 0;
}

bool getBool(const String& value)
{
    String valueLower = value;
    StringUtil::toLowerCase(valueLower);
    if (valueLower == "false" || value == "no" || value == "0")
        return false;
    else
        return true;
}   

Real GetRealAttribute(const TiXmlElement* xmlElement, const char* name, Real defaultValue = 0)
{
    String value = xmlElement->Attribute(name);
    return value.empty() ? defaultValue : StringConverter::parseReal(value);    
}

int GetIntAttribute(const TiXmlElement* xmlElement, const char* name, int defaultValue = 0)
{
    String value = xmlElement->Attribute(name);
    return value.empty() ? defaultValue : StringConverter::parseInt(value);
}

Vector3 LoadXYZ(const TiXmlElement* objectElement)
{
    Vector3 xyz;
    xyz.x = GetRealAttribute(objectElement, "x");
    xyz.y = GetRealAttribute(objectElement, "y");
    xyz.z = GetRealAttribute(objectElement, "z");
    return xyz;
}

Light::LightTypes ParseLightType(const String& type)
{
    String typeLower = type;
    StringUtil::toLowerCase(typeLower);

    if (typeLower == "point")
        return Light::LT_POINT;
    else if (typeLower == "directional")
        return Light::LT_DIRECTIONAL;
    else if (typeLower == "spot")
        return Light::LT_SPOTLIGHT;

    StringUtil::StrStreamType errorMessage;
    errorMessage << "Invalid light type specified: " << type;

    OGRE_EXCEPT
        (
        Exception::ERR_INVALIDPARAMS,
	    errorMessage.str(), 
	    "OgreMaxUtilities::ParseLightType"
        );
}

Quaternion LoadRotation(const TiXmlElement* objectElement)
{
    Quaternion rotation = Quaternion::IDENTITY;

        rotation.x = GetRealAttribute(objectElement, "qx", 1);
        rotation.y = GetRealAttribute(objectElement, "qy", 0);
        rotation.z = GetRealAttribute(objectElement, "qz", 1);
        rotation.w = GetRealAttribute(objectElement, "qw", 0);
   
	return rotation;
}

ColourValue LoadColor(const TiXmlElement* objectElement)
{   
    ColourValue color;
    color.r = GetRealAttribute(objectElement, "r", 0);
    color.g = GetRealAttribute(objectElement, "g", 0);
    color.b = GetRealAttribute(objectElement, "b", 0);
    color.a = 1;
    return color;
}

String GetStringAttribute(const TiXmlElement* xmlElement, const char* name)
{
    const char* value = xmlElement->Attribute(name);
    if (value != 0)
        return value;
    else
        return StringUtil::BLANK;    
}

void LoadLightAttenuation(const TiXmlElement* objectElement, Light* light)
{
	
    String value;

    value = GetStringAttribute(objectElement, "range");
    Real range = value.empty() ? light->getAttenuationRange() : StringConverter::parseReal(value);

    value = GetStringAttribute(objectElement, "constant");
    Real constant = value.empty() ? light->getAttenuationConstant() : StringConverter::parseReal(value);

    value = GetStringAttribute(objectElement, "linear");
    Real linear = value.empty() ? light->getAttenuationLinear() : StringConverter::parseReal(value);

    value = GetStringAttribute(objectElement, "quadric");
    Real quadric = value.empty() ? light->getAttenuationQuadric() : StringConverter::parseReal(value);

    light->setAttenuation(range, constant, linear, quadric);    
}

void LoadLightRange(const TiXmlElement* objectElement, Light* light)
{

    if (light->getType() == Light::LT_SPOTLIGHT)
    {
        String value;

        value = GetStringAttribute(objectElement, "inner");
        if (!value.empty())
            light->setSpotlightInnerAngle(Radian(StringConverter::parseReal(value)));

        value = GetStringAttribute(objectElement, "outer");
        if (!value.empty())
            light->setSpotlightOuterAngle(Radian(StringConverter::parseReal(value)));

        value = GetStringAttribute(objectElement, "falloff");
        if (!value.empty())
            light->setSpotlightFalloff(StringConverter::parseReal(value));
    }
}

void GetChildText(const TiXmlElement* xmlElement, String& text)
{
    //Get the first element 
    const TiXmlNode* childNode = xmlElement->FirstChild();
    while (childNode != 0)
    {
        if (childNode->Type() == TiXmlNode::TEXT)
        {
            const TiXmlText* textNode = childNode->ToText();
            if (textNode != 0)
            {
                text = textNode->Value();
                break;
            }
        }
        childNode = xmlElement->NextSibling();
    }    
}

void loadPlane(const TiXmlElement* planeElement,SceneNode* node, Ogre::SceneManager *mSceneMgr)
{   


	Ogre::Vector3 normal;
	Ogre::Vector3 upVector;

    const TiXmlElement* childElement = 0;
    while (childElement = IterateChildElements(planeElement, childElement))
    {
	

        String elementName = childElement->Value();
      
		if (elementName == "normal")
			normal=LoadXYZ(childElement);
        else if (elementName == "upVector")
			upVector=LoadXYZ(childElement);
       
    }

	Ogre::Plane plane(normal, GetRealAttribute(planeElement, "distance"));
	MeshManager::getSingleton().createPlane
        (
        planeElement->Attribute("name"), 
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        plane,
        GetRealAttribute(planeElement, "width"), GetRealAttribute(planeElement, "height"),
        GetIntAttribute(planeElement, "xSegments"), GetIntAttribute(planeElement, "ySegments"),
        getBool(planeElement->Attribute("normals")), GetIntAttribute(planeElement, "numTexCoordSets"),
        GetRealAttribute(planeElement, "uTile"), GetRealAttribute(planeElement, "vTile"),
        upVector
        );

	Ogre::Entity* ent;
	ent= mSceneMgr->createEntity( planeElement->Attribute("name"), planeElement->Attribute("name") );
	ent->getMesh()->buildTangentVectors();
	node->attachObject( ent );
	ent->setCastShadows(getBool(planeElement->Attribute("castShadows")));
	ent->setMaterialName(planeElement->Attribute("material"));
}

void loadLight(const TiXmlElement* lightElement,SceneNode* node, Ogre::SceneManager *mSceneMgr)
{   


    //Create the light
    Light* light = mSceneMgr->createLight(lightElement->Attribute("name"));
    light->setType(ParseLightType(GetStringAttribute(lightElement, "type")));
    light->setCastShadows(getBool(lightElement->Attribute("castShadows")));
    light->setPowerScale(GetRealAttribute(lightElement, "power"));
	node->attachObject(light);

    const TiXmlElement* childElement = 0;
    while (childElement = IterateChildElements(lightElement, childElement))
    {
	

        String elementName = childElement->Value();
      
		if (elementName == "colourDiffuse")
			light->setDiffuseColour(LoadColor(childElement));
        else if (elementName == "colourSpecular")
			light->setSpecularColour(LoadColor(childElement));
        else if (elementName == "lightRange")
			LoadLightRange(childElement, light);
        else if (elementName == "lightAttenuation")
            LoadLightAttenuation(childElement, light);
        else if (elementName == "position")
            light->setPosition(LoadXYZ(childElement));
        else if (elementName == "normal")
            light->setDirection(LoadXYZ(childElement));    
    }
}

void loadEntity(const TiXmlElement* entityElement,SceneNode* node, bool visible, Ogre::SceneManager *mSceneMgr,NxOgre::Scene* mScene, Critter::RenderSystem* mRenderSystem)
{
		Ogre::Real mass = 0.5;

		Entity* ent;	
		ent = mSceneMgr->createEntity( entityElement->Attribute("name"), entityElement->Attribute("meshFile") );
		node->attachObject( ent );
		ent->setVisible(visible);

		String str = GetStringAttribute(entityElement,"castShadows");
		if( !str.empty() && !getBool(str)) ent->setCastShadows(false); else ent->setCastShadows(true);
	
		const TiXmlElement* subentityElement=entityElement->FirstChildElement("subentities");
		const TiXmlElement* childElement = 0;
		while (childElement = IterateChildElements(subentityElement, childElement))
		{
			int index = GetIntAttribute(childElement, "index", 0);
			ent->getSubEntity(index)->setMaterialName(GetStringAttribute(childElement, "materialName"));
		}

		
		const TiXmlElement* userdataElement=entityElement->FirstChildElement("userData");

		if (userdataElement!=NULL)
		{
			Ogre::String userData;
			GetChildText(userdataElement,userData);

			Ogre::Log* myLog = Ogre::LogManager::getSingleton().getDefaultLog();
			//myLog->logMessage(userData,LML_NORMAL);

			TiXmlDocument document;
			document.Parse(userData.c_str());
			if (!document.Error())
			{
			TiXmlElement *root=document.RootElement();
			if (root->Value()!=NULL)
			{
			Ogre::String rootTag(root->Value());


			if (rootTag == "PhysicalBody")
				{

				const TiXmlElement* childElemen;
				const TiXmlElement* childElemens;
				childElemen=root->FirstChildElement();
				childElemens=childElemen->NextSiblingElement();
				String elementName=childElemen->GetText();
				myLog->logMessage(rootTag,LML_NORMAL);
				myLog->logMessage(elementName,LML_NORMAL);

				if (elementName=="conv")
				{
   

					Ogre::String filepath("../../media/physics/lvl2/");

					if(rebuildMesh){
					    Ogre::MeshPtr mPtr = ent->getMesh()->clone("uniq"+Ogre::StringConverter::toString(pocitadlo++));
						rescaleMeshPlease(mPtr,node->getScale());
						NxOgre::MeshData* mesh_data = Critter::MeshFunctions::read(mPtr);
						mesh_data->mType = NxOgre::Enums::MeshType_Convex;
						mesh_data->cook("file://"+filepath+ent->getName()+".nxs");
						delete mesh_data;
						mPtr.freeMethod();
					}

										
				  Critter::BodyDescription bodyDescription;
				  bodyDescription.mMass = 0.5f;
				  NxOgre::Mesh* mMesh = NxOgre::MeshManager::getSingleton()->load("file://"+filepath+ent->getName()+".nxs","uniq"+Ogre::StringConverter::toString(pocitadlo++));
				  Critter::Body* mBody= mRenderSystem->createBody(NxOgre::ConvexDescription(mMesh), NxOgre::Matrix44(NxOgre::Vec3(node->_getDerivedPosition()),NxOgre::Quat(node->_getDerivedOrientation())), bodyDescription);
				  mBody->setSceneNode(node);
				  mBody->getShape(0)->setSkinWidth(0.01);
				  
				  /*	OgreNewt::ConvexCollisionPtr col = OgreNewt::ConvexCollisionPtr(new OgreNewt::CollisionPrimitives::ConvexHull(mWorld, ent,0));
							
							// then make the rigid body.
							OgreNewt::Body* body = new OgreNewt::Body( mWorld, col );

							Ogre::Vector3 inertia, offset;
							col->calculateInertialMatrix(inertia, offset);
				#ifdef OGRENEWT_NO_COLLISION_SHAREDPTR
							//no longer need the collision shape object
							delete col;
				#endif

							myLog->logMessage("matID je:",LML_NORMAL);
						myLog->logMessage(childElemens->GetText(),LML_NORMAL);
							int mat=Ogre::StringConverter::parseInt(childElemens->GetText());

							body->setUserData(Ogre::Any(mat));
							childElemens=childElemens->NextSiblingElement();

										myLog->logMessage("mass je:",LML_NORMAL);
						myLog->logMessage(childElemens->GetText(),LML_NORMAL);
							Real mass=Ogre::StringConverter::parseReal(childElemens->GetText());
							body->setMassMatrix( mass, inertia );
							body->setType(Ogre::StringConverter::parseInt(childElemens->NextSiblingElement()->GetText()));
							if(body->getType()==2) body->setMaterialGroupID(flag_mat);
							body->setCenterOfMass(offset);
							//body->setContinuousCollisionMode(1);
							//body->setAngularDamping(Vector3(0,0,0));
							body->setLinearDamping(0.3);
							//body->setAutoSleep(0);
							//body->setMaterialGroupID();
							// attach to the scene node.
							body->setPositionOrientation(node->_getDerivedPosition(), node->_getDerivedOrientation() );
							body->attachNode( node );
							
								
					 body->setStandardForceCallback();
*/
				}
				if (elementName=="tree"){

					/*OgreNewt::CollisionPtr col = OgreNewt::CollisionPtr(new OgreNewt::CollisionPrimitives::TreeCollision( mWorld, ent, false, 0 ));
					OgreNewt::Body* bod = new OgreNewt::Body( mWorld, col );
					
				#ifdef OGRENEWT_NO_COLLISION_SHAREDPTR
					delete col;
				#endif

					int mat=Ogre::StringConverter::parseInt(childElemens->GetText());
					bod->setUserData(Ogre::Any(mat));
					bod->setPositionOrientation( node->_getDerivedPosition(), node->_getDerivedOrientation() );
					bod->attachNode( node );
					bod->setType(Ogre::StringConverter::parseInt(childElemens->NextSiblingElement()->GetText()));*/

					Ogre::String filepath("../../media/physics/lvl2/");

					if(rebuildMesh){
					    Ogre::MeshPtr mPtr = ent->getMesh()->clone("uniq"+Ogre::StringConverter::toString(pocitadlo++));
						rescaleMeshPlease(mPtr,node->getScale());
						NxOgre::MeshData* mesh_data2 = Critter::MeshFunctions::read(mPtr);
						mesh_data2->mType = NxOgre::Enums::MeshType_Triangle;
						mesh_data2->cook("file://"+filepath+ent->getName()+".nxs");
						delete mesh_data2;
						mPtr.freeMethod();
					}

					NxOgre::Mesh* mBody = NxOgre::MeshManager::getSingleton()->load("file://"+filepath+ent->getName()+".nxs", ent->getName());
						NxOgre::SceneGeometry* ter=mScene->createSceneGeometry(NxOgre::TriangleGeometryDescription(mBody), NxOgre::Matrix44(NxOgre::Vec3(node->_getDerivedPosition()),NxOgre::Quat(node->_getDerivedOrientation())));
					ter->getShape(0)->setSkinWidth(0.01);

				}

				if (elementName=="box"){
				
				  Critter::BodyDescription bodyDescription;
				  bodyDescription.mMass = 0.5f;
				  Critter::Body* mBody= mRenderSystem->createBody(NxOgre::BoxDescription(2,3,1), NxOgre::Matrix44(NxOgre::Vec3(node->_getDerivedPosition())), bodyDescription);
				  mBody->setSceneNode(node);
				  //centerBoxPlease(ent->getMesh(),3);
				  mBody->getShape(0)->setSkinWidth(0.01);
				  
				}


			}
			}
			}
		}

				

		

}

void loadNode(const TiXmlElement* nodeElement, Ogre::SceneManager *mSceneMgr,NxOgre::Scene* mScene, Critter::RenderSystem* mRenderSystem)
{

	/*Ogre::Vector3 position(0,0,0);
	Ogre::Vector3 scale(1,1,1);
	Ogre::Quaternion rotation(0,0,0,1);
	Ogre::Entity* ent=NULL;*/
	SceneNode* node;
	Ogre::String name;
	name = nodeElement->Attribute("name");
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode( name );

		
	bool visible=true;
	if(nodeElement->Attribute("visibility")!=NULL){
			Ogre::String v=nodeElement->Attribute("visibility");
			if (v=="hidden") visible=false; }
	

	 String elementName;
    const TiXmlElement* childElement = 0;
    while (childElement = IterateChildElements(nodeElement, childElement))
    {
        elementName = childElement->Value();
        
        if (elementName == "position")
            node->setPosition(LoadXYZ(childElement));
        else if (elementName == "rotation")
            node->setOrientation(LoadRotation(childElement));
        else if (elementName == "scale")
            node->setScale(LoadXYZ(childElement));
        else if (elementName == "entity")
            loadEntity(childElement, node,visible,mSceneMgr,mScene,mRenderSystem);
        else if (elementName == "light")
            loadLight(childElement, node,mSceneMgr);
		else if (elementName == "plane")
            loadPlane(childElement, node,mSceneMgr);
    }

}

void loadScene(Ogre::String filename, Ogre::SceneManager *mSceneMgr,NxOgre::Scene* mScene, Critter::RenderSystem* mRenderSystem)
{
	TiXmlDocument document;
	document.LoadFile(filename.c_str());
	const TiXmlElement* mainElement = document.RootElement();
	const TiXmlElement* nodesElement = mainElement->FirstChildElement("nodes");



	 String elementName;
    const TiXmlElement* childElement = 0;
    while (childElement = IterateChildElements(nodesElement, childElement))
    {
        elementName = childElement->Value();
        
        if (elementName == "node")
            loadNode(childElement,mSceneMgr,mScene,mRenderSystem);
        /*else if (elementName == "skyBox")
            LoadSkyBox(childElement);*/
    }
}